package org.leviatan.dataharbour.core.model.control;

import java.util.HashMap;
import java.util.Iterator;

import org.leviatan.common.xml.XMLIO;
import org.leviatan.dataharbour.core.model.DataEntity;
import org.leviatan.dataharbour.core.model.HarbourRequest;
import org.leviatan.dataharbour.core.model.impl.DataEntityImpl;
import org.leviatan.dataharbour.core.model.impl.HarbourRequestImpl;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

/** 
 * This class handles storage requests to dataharbour. It is the single point
 * through which outsiders makes requests to dataharbour core.
 * 
 * @author leviatan
 *
 */
public abstract class HarbourRequestHandler {
	
	/**
	 * This is the single point to recieve data for harbouring from outside. 
	 * This method is what the ejbs are gonna call.
	 * @param request
	 */
	public static void handleRequest(Object request) {
		
		HarbourRequest requestModel = HarbourRequestHandler.parseIncomingRequest(request);
		
		String senderId = requestModel.getSenderId().trim();
		
		/*

		// Sender don't exists?
		if ( !RegisterHandler.senderExists(senderId) ) {
			// if it's a new sender id -> add it to register
			RegisterHandler.addSenderId(senderId);
		}
		
		Collection dataSources = RegisterHandler.getDataSources(requestModel);
		
		// sender has datasource connected?
		if (dataSources != null && !dataSources.isEmpty()) {
			// let datasource handle harbouring
			String dsId;
			DataSource ds;
			for (Iterator i = dataSources.iterator(); i.hasNext(); ) {
				dsId = i.next().toString();
				
				//ds = RegisterHandler.getDataSourceModel(dsId);
				if (ds != null) {
					DataSourceHandler.handleRequest(ds, requestModel);
				}
			}
		} else {
			// create internal file system id
			HarbourRequestHandler.assignGeneratedId(requestModel);
			
			// prepare variables, they are accessable through their containing request model
			for (Iterator i = requestModel.getDataEntityMap().keySet().iterator(); i.hasNext(); ) {
				String name = i.next().toString();
				String value = requestModel.getDataEntityMap().get(name).toString();
				
				String id = requestModel.getId() + name;
				
				DataEntity dataEntity = new DataEntityImpl();
				dataEntity.setId(id);
				dataEntity.setDataName(name);
				dataEntity.setDataValue(value);
				dataEntity.setDataType(true); // txt
				dataEntity.setHarbourRequestId(requestModel.getId());
			}
			
			// IMPORTANT !!! //
			HarbourRequestHandler.assignGeneratedId(requestModel);
			HarbourRequestHandler.assignGeneratedIdsForDataUnits(requestModel);
			// !!! //
			
			//	store request in temp file system.
			//RegisterHandler.addHarbourRequestModel(requestModel);
			//RegisterHandler.addUnpersistedSendersHarbourRequest(senderId, requestModel);
		}
		
		*/
	}
	
	public static HarbourRequest parseIncomingRequest(Object request) {
		Document doc = XMLIO.getInstance().readXMLString((String)request);
		return HarbourRequestHandler.parseIncomingRequestDOM(doc);
	}
	
	public static HarbourRequest parseIncomingRequestDOM(Document doc) {
		Element elem;
		
		String senderId = 
			((Element)doc.getElementsByTagName("sender").item(0)).getAttribute("id");
		String actorId = 
			((Element)doc.getElementsByTagName("actor").item(0)).getAttribute("id");
		
		elem = (Element)doc.getElementsByTagName("task").item(0);
		String taskId = elem.getAttribute("id");
		String taskName = elem.getAttribute("name");
		
		elem = (Element)doc.getElementsByTagName("process-definition").item(0);
		String processDefId = elem.getAttribute("id");
		String processDefName = elem.getAttribute("name");
		String processDefVersion = elem.getAttribute("version");
		
		elem = (Element)doc.getElementsByTagName("process-instance").item(0);
		String processInstanceId = elem.getAttribute("id");
		
		HashMap<String, DataEntity> variableMap = new HashMap<String, DataEntity>();
		NodeList nodeList = doc.getElementsByTagName("var");
		
		for (int i = 0; i < nodeList.getLength(); i++) {
			Element dataEntityElem = (Element) nodeList.item(i);
			String varName, varValue;
			
			varName = dataEntityElem.getElementsByTagName("name").item(0).getTextContent().trim();
			varValue = dataEntityElem.getElementsByTagName("value").item(0).getTextContent().trim();
			
			DataEntity dataEnt = new DataEntityImpl();
			dataEnt.setDataName(varName);
			dataEnt.setDataValue(varValue);
			
			variableMap.put(varName, dataEnt);
			
		}
		
		HarbourRequest harbourRequest = new HarbourRequestImpl();
		harbourRequest.setSenderId( senderId );
		harbourRequest.setActorId(actorId);
		harbourRequest.setTaskId(taskId);
		harbourRequest.setTaskName(taskName);
		harbourRequest.setProcessId(processDefId);
		harbourRequest.setProcessName(processDefName);
		harbourRequest.setProcessVersion(Integer.parseInt(processDefVersion));
		harbourRequest.setProcessInstanceId(processInstanceId);
		
		harbourRequest.setDataEntityMap(variableMap);
		
		return harbourRequest;
	}
	
	/**
	 * Generates and sets an id for specified HarbourRequest. Uses <link>
	 * org.leviatan.common.keyutils.KeyGenerator</link>.
	 * @param requestMdl
	 */
	public static void assignGeneratedId(HarbourRequest requestMdl) {
		String prefix = requestMdl.getSenderId();
		String postfix = org.leviatan.common.keyutils.KeyGenerator.generateUniqueKeyAsString();
		requestMdl.setId(prefix.concat(postfix));
	}
	
	/**
	 * Sets internal temp ids for all PersistableDataEntities contained in a HarbourRequest.
	 * 
	 * @param requestMdl
	 */
	public static void assignGeneratedIdsForDataUnits(HarbourRequest requestMdl) {
		for (Iterator i = requestMdl.getDataEntityMap().values().iterator(); i.hasNext(); ) {
			DataEntity dataEntity = (DataEntity) i.next();
			HarbourRequestHandler.prepareDataEntity(dataEntity, requestMdl.getId());
		}
	}
	
	/**
	 * Sets an internal temp id for a PersistableDataEntity.
	 * 
	 * Requirements: dataName must be set, != null.
	 * 
	 * @param data
	 * @param harbourRequestId
	 */
	public static void prepareDataEntity(DataEntity data, String harbourRequestId) {
		String id = harbourRequestId + "?" + data.getDataName();
		data.setId(id);
		data.setDataType(true); //txt
		data.setHarbourRequestId(harbourRequestId);
	}
	
	public static HarbourRequest instantiateModel(Element e) {
		HarbourRequest hr = new HarbourRequestImpl();
		String id = e.getElementsByTagName("id").item(0).getTextContent();
		String senderId = e.getElementsByTagName("sender-id").item(0).getTextContent();
		
		hr.setId(id);
		hr.setSenderId(senderId);
		
		return hr;
	}
	
	public static String convertToXml(HarbourRequest hr) {
		String xml = "";
		xml += "\n\t\t<id><![CDATA["+ hr.getId()+ "]]></id>";
		xml += "\n\t\t<sender-id><![CDATA["+ hr.getSenderId()+ "]]></sender-id>";
		return xml;
	}

}
